package xyz.labradormm.spider.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import xyz.labradormm.spider.config.ApplicationException;
import xyz.labradormm.spider.constant.RedisKeyPrefix;
import xyz.labradormm.spider.domain.dto.DownloadVideoDto;
import xyz.labradormm.spider.domain.dto.ForkJoinTaskDto;
import xyz.labradormm.spider.domain.vo.DownloadVideoResultVo;
import xyz.labradormm.spider.domain.vo.YoutubeItemVo;
import xyz.labradormm.spider.enums.SpiderType;
import xyz.labradormm.spider.enums.StatusCode;
import xyz.labradormm.spider.enums.VideoQualityLabel;
import xyz.labradormm.spider.service.IUploadService;
import xyz.labradormm.spider.util.HttpUtils;
import xyz.labradormm.spider.util.RequestWrap;
import xyz.labradormm.spider.util.ResponseWrap;
import xyz.labradormm.spider.util.forkjoin.YtVideoDownloadFJTask;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;

import static xyz.labradormm.spider.util.TryUtil.tryGetValue;

/**
 * @author huanghaojian
 * @description
 * @date 2023-12-10 2:15
 */
@Service
@Slf4j
public class TaskService {


    @Autowired
    private HttpHost httpHost;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    @Qualifier(value = "TencentUploadService")
    private IUploadService uploadService;

    public DownloadVideoResultVo youtubeDownload(DownloadVideoDto dto) {
        RequestWrap requestWrap = HttpUtils.getInstance().get(dto.getTargetUrl());
        requestWrap.setProxy(httpHost);
        requestWrap.addHeader("User_Agent", HttpUtils.User_Agent);
        String cookie = redisTemplate.opsForValue().get(String.format(RedisKeyPrefix.SPIDER_CONFIG_AUTH_INFO, SpiderType.youtube.getCode()));
        requestWrap.addHeader("cookie", cookie);
        ResponseWrap responseWrap = requestWrap.execute();
        String resStr = responseWrap.getString();
        YoutubeItemVo ytVideo = getYtVideo(resStr);
        DownloadVideoResultVo resultVo = new DownloadVideoResultVo();
        resultVo.setTitle(ytVideo.getTitle());
        resultVo.setThumbnails(ytVideo.getThumbnails());
        if (StringUtils.isNotEmpty(resultVo.getThumbnails())) {
            RequestWrap get = HttpUtils.getInstance().get(resultVo.getThumbnails());
            get.setConnectTimeout(3600000);
            get.setProxy(httpHost);
            get.addHeader("User_Agent", HttpUtils.User_Agent);
            get.addHeader("cookie", cookie);
            ResponseWrap response = requestWrap.execute();
            resultVo.setThumbnails(uploadService.uploadImg(response.getByteArray(), null));
        }
        String videoUrl = ytVideo.getResourceList().get(dto.getQualityLabel());
        if (StringUtils.isEmpty(videoUrl)) {
            videoUrl = ytVideo.getResources();
        }
        byte[] resourceBytes = downloadVideoProcess(videoUrl);
        log.info("视频下载完成---------------------------------开始上传");
        String videoDownloadUrl = uploadService.uploadVideo(resourceBytes, null);
        if (StringUtils.isEmpty(videoDownloadUrl)) {
            throw new ApplicationException(StatusCode.SYS_COMMON_OPERATION_FAIL, "视频下载失败!");
        }
        resultVo.setVideoDownloadUrl(videoDownloadUrl);
        /*if (dto.getGenerateAudioSwitch() == 1) {
            *//**
             * 开始转换音频
             *//*
            try {
                byte[] mp3Bytes = FFmpegUtil.MP4_to_MP3(resourceBytes, "temp_file_" + RandomUtil.randomString(8));
                if (mp3Bytes != null) {
                    String audioDownloadUrl = uploadService.uploadTemp(mp3Bytes, uploadService.createDefaultKey().concat(".mp3"));
                    resultVo.setAudioDownloadUrl(audioDownloadUrl);
                }
            } catch (Exception e) {
                log.error("音频转换异常-------------------", e);
            }
        }*/
        return resultVo;
    }

    public byte[] downloadVideoProcess(String videoUrl) {
        try {
            Long contentLength = getResourceContentLength(videoUrl);
            log.info("开始解析视频，视频字节长度=" + contentLength);
            if (contentLength > (500 * 1024 * 1024)) {
                log.info(String.format("[%s]视频大小超过500M，取消下载", videoUrl));
                return null;
            } else {
                String cookie = redisTemplate.opsForValue().get(String.format(RedisKeyPrefix.SPIDER_CONFIG_AUTH_INFO, SpiderType.youtube.getCode()));
                ForkJoinTaskDto vpnRequestForm = ForkJoinTaskDto.builder().vpnProxy(httpHost)
                        .targetUrl(videoUrl)
                        .cookie(cookie)
                        .resourceLength(contentLength)
                        .build();
                ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
                ForkJoinTask<byte[]> taskFuture = forkJoinPool.submit(new YtVideoDownloadFJTask(0L, contentLength, vpnRequestForm));
                byte[] resourceBytes = taskFuture.get();
                return resourceBytes;
            }
        } catch (Exception e) {
            log.error("下载视频失败", e);
        }
        return null;
    }

    public Long getResourceContentLength(String targetUrl) {
        RequestWrap requestWrap = HttpUtils.getInstance().head(targetUrl);
        requestWrap.setProxy(httpHost);
        requestWrap.setConnectTimeout(5000);
        requestWrap.addHeader("User_Agent", HttpUtils.User_Agent);
        String cookie = redisTemplate.opsForValue().get(String.format(RedisKeyPrefix.SPIDER_CONFIG_AUTH_INFO, SpiderType.youtube.getCode()));
        requestWrap.addHeader("cookie", cookie);
        ResponseWrap responseWrap = requestWrap.execute();
        Header[] headers = responseWrap.getHeaders("Content-Length");
        return Long.valueOf(headers[0].getValue());
    }

    public YoutubeItemVo getYtVideo(String html) {
        Document document = Jsoup.parse(html);
        Elements scripts = document.getElementsByTag("script");
        String scriptStr = "";
        for (Element script : scripts) {
            if (script.html().contains("var ytInitialPlayerResponse =")) {
                scriptStr = script.html();
                break;
            }
        }
        if (StringUtils.isEmpty(scriptStr)) {
            return null;
        }
        YoutubeItemVo youtubeItemVo = new YoutubeItemVo();
        scriptStr = scriptStr.replace("var ytInitialPlayerResponse =", "");
        scriptStr = scriptStr.substring(0, scriptStr.indexOf(";var") == -1 ? scriptStr.length() - 1 : scriptStr.indexOf(";var"));
        JSONObject data = JSON.parseObject(scriptStr);
        youtubeItemVo.setTitle(tryGetValue(data, 0, (obj, index) -> data.getJSONObject("videoDetails").getString("title")));
        youtubeItemVo.setThumbnails(tryGetValue(data, 0, (obj, index) -> {
            JSONArray thumbnails = data.getJSONObject("videoDetails").getJSONObject("thumbnail").getJSONArray("thumbnails");
            return thumbnails.getJSONObject(thumbnails.size() - 1).getString("url");
        }));
        youtubeItemVo.setPublishDate(tryGetValue(data, 0, (obj, index) -> data.getJSONObject("microformat").getJSONObject("playerMicroformatRenderer").getString("publishDate")));
        youtubeItemVo.setDescription(tryGetValue(data, 0, (obj, index) -> data.getJSONObject("videoDetails").getString("shortDescription")));
        Map<String, JSONObject> resources = new HashMap<>();
        Map<String, String> videoUrlMap = new HashMap<>();
        JSONArray videoItems = data.getJSONObject("streamingData").getJSONArray("formats");
        for (int i = 0; i < videoItems.size(); i++) {
            JSONObject videoItem = videoItems.getJSONObject(i);
            resources.put(videoItem.getString("qualityLabel"), videoItem);
            videoUrlMap.put(videoItem.getString("qualityLabel"), videoItem.getString("url"));
        }
        JSONObject videoItem = null;
        if (resources.containsKey(VideoQualityLabel.清晰.getCode())) {
            videoItem = resources.get(VideoQualityLabel.清晰.getCode());
        } else if (resources.containsKey(VideoQualityLabel.高清.getCode())) {
            videoItem = resources.get(VideoQualityLabel.高清.getCode());
        } else if (resources.containsKey(VideoQualityLabel.超清.getCode())) {
            videoItem = resources.get(VideoQualityLabel.超清.getCode());
        } else if (resources.containsKey(VideoQualityLabel.一般.getCode())) {
            videoItem = resources.get(VideoQualityLabel.一般.getCode());
        } else if (resources.containsKey(VideoQualityLabel.偏低.getCode())) {
            videoItem = resources.get(VideoQualityLabel.偏低.getCode());
        } else if (resources.containsKey(VideoQualityLabel.极低.getCode())) {
            videoItem = resources.get(VideoQualityLabel.极低.getCode());
        }
        if (videoItem == null) {
            return null;
        }
        youtubeItemVo.setResourceList(videoUrlMap);
        youtubeItemVo.setPublishTime(videoItem.getString("lastModified"));
        youtubeItemVo.setResources(videoItem.getString("url"));
        youtubeItemVo.setViewCount(tryGetValue(data, 0, (obj, index) -> data.getJSONObject("videoDetails").getString("viewCount")));
        return youtubeItemVo;
    }


}
